package com.mostsnails.common.base.utils.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mostsnails.common.base.utils.common.RequestUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Controller 请求记录
 *
 * @Author MostSnails
 * @Version 1.0
 */
@Aspect
public class WebLogAspect {
    private static org.slf4j.Logger log = LoggerFactory.getLogger(WebLogAspect.class);

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 切入点 请求注解 也可以加入自定义注解
     */
    @Pointcut(
            "@annotation(org.springframework.web.bind.annotation.RequestMapping) || " +
                    "@annotation(org.springframework.web.bind.annotation.GetMapping)||" +
                    "@annotation(org.springframework.web.bind.annotation.PostMapping)" +
                    "@annotation(org.springframework.web.bind.annotation.DeleteMapping)" +
                    "@annotation(org.springframework.web.bind.annotation.PutMapping)"
    )
    protected void webLog() {

    }

    /**
     * 得到当前请求需要记录的信息
     *
     * @param request
     * @return
     */
    protected String getHttpServletRequestLogInfoStr(HttpServletRequest request) {
        // 记录下请求内容
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(String.format("开始请求URL[%s],请求IP[%s]", RequestUtil.getRequestUrl(),  RequestUtil.getIpAddr(request)));
        //可以获取请求头 比如自定义的(租户ID)
//        Enumeration<String> headerNames = request.getHeaderNames();
//        if (headerNames != null) {
//            StringBuilder headStr = new StringBuilder();
//            while (headerNames.hasMoreElements()) {
//                String name = headerNames.nextElement();
//                String header = request.getHeader(name);
//                headStr.append(name).append(":").append(header).append("\n");
//            }
//            stringBuilder.append(String.format("header: %s", headStr.toString()));
//        }
        //请求参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (parameterMap != null && !parameterMap.isEmpty()) {
            StringBuilder strBuilder = new StringBuilder();
            parameterMap.forEach((key, value) -> {
                try {
                    strBuilder.append(key).append("=").append(objectMapper.writeValueAsString(value)).append(";");
                } catch (JsonProcessingException e) {
                    //忽略异常
                }
            });
            if (strBuilder.length() > 0) {
                stringBuilder.append("请求参数:----").append(strBuilder.toString());
            }
        }
        //这里得到body里面的数据
        if (request instanceof MultipartHttpServletRequest) {
            stringBuilder.append("\n可能存在文件上传:");
            MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
            MultiValueMap<String, MultipartFile> multiFileMap = multipartHttpServletRequest.getMultiFileMap();
            stringBuilder.append("[");
            multiFileMap.forEach((k, v) -> {
                stringBuilder.append("文件名:" + k).append(",文件大小:").append(v.size()).append(";");
            });
            stringBuilder.append("]");
        }
        return stringBuilder.toString();
    }

    /**
     * 得到当前代理方法 需要记录的信息
     * 方便用于子类重写
     *
     * @param joinPoint
     * @return
     */
    protected String getJoinPointLogInfoStr(JoinPoint joinPoint) {
        if (joinPoint.getSignature() instanceof MethodSignature) {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //得到调用的方法对象
            Method method = methodSignature.getMethod();
            //得到此方法具有的注解列表
            Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
            //得到调用的方法名
            String name = method.getName();
            //得到方法的参数名
            String[] parameterNames = methodSignature.getParameterNames();
            //得到方法的参数类型
            Class<?>[] parameterTypes = methodSignature.getParameterTypes();
            //得到方法中的每个参数的注解列表
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            //得到当前方法的参数值
            Object[] args = joinPoint.getArgs();
            StringBuilder stringBuilder = new StringBuilder("执行的方法名:" + name);
//            //得到方法上注解的说明并加入日志中  可以自定义注解
//            MethodLogDesc methodLogDesc = method.getAnnotation(MethodLogDesc.class);
//            if (methodLogDesc != null && StringUtils.isNotEmpty(methodLogDesc.value())) {
//                stringBuilder.append("(").append(methodLogDesc.value()).append(")");
//            }
            stringBuilder.append("[");
            if (parameterNames != null && parameterNames.length > 0) {
                //开始遍历拼接
                for (int i = 0; i < parameterNames.length; i++) {
                    String parameterName = parameterNames[i];
                    stringBuilder.append(parameterName).append("=");
                    try {
                        if (args[i] != null) {
                            stringBuilder.append(objectMapper.writeValueAsString(args[i]));
                        } else {
                            stringBuilder.append("null");
                        }
                    } catch (JsonProcessingException e) {
                        //忽略异常
                        stringBuilder.append("notConvertJsonString");
                    }
                    stringBuilder.append(";");
                }
            }
            stringBuilder.append("]");
            return stringBuilder.toString();
        }
        return "";
    }


    @Around(value = "webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        StringBuilder stringBuilder = new StringBuilder();
        // 接收到请求，记录请求内容
        HttpServletRequest request = RequestUtil.getHttpServletRequest();
        String requestLogInfoStr = getHttpServletRequestLogInfoStr(request);
        if (StringUtils.isNotEmpty(requestLogInfoStr)) {
            stringBuilder.append(requestLogInfoStr).append("\n");
        }
        //这里打印执行的方法的参数信息
//        String signatureLogInfoStr = getJoinPointLogInfoStr(proceedingJoinPoint);
//        if (StringUtils.isNotEmpty(signatureLogInfoStr)) {
//            stringBuilder.append(signatureLogInfoStr);
//        }
        log.info(stringBuilder.toString());
        long startTime = System.currentTimeMillis();
        //执行目标方法
        Object proceed = proceedingJoinPoint.proceed();
        long runTime = System.currentTimeMillis() - startTime;
        stringBuilder = new StringBuilder();
        stringBuilder.append("执行完成! ")
//                .append(signatureLogInfoStr)
                .append("\nURL:").append(RequestUtil.getRequestUrl())
                .append("\n返回值类型: ").append(proceed.getClass().getSimpleName())
//                .append("\n返回值: ").append(objectMapper.writeValueAsString(proceed))
                .append("\n执行时间: ").append(runTime).append("ms");
        log.info(stringBuilder.toString());
        return proceed;
    }

    /**
     * 异常通知 参数 Throwable ex 一定要在配置中指出. throwing指出参数名
     * 如果产生异常 此通知是最后执行的
     *
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing(pointcut = "webLog()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("执行出异常! ");
        //这里打印执行的方法的参数信息
        String signatureLogInfoStr = getJoinPointLogInfoStr(joinPoint);
        if (StringUtils.isNotEmpty(signatureLogInfoStr)) {
            stringBuilder.append(signatureLogInfoStr);
        }
        stringBuilder.append("\nURL:").append(RequestUtil.getRequestUrl())
                .append("\n异常类型: ").append(ex.getClass().getSimpleName())
                .append("\n异常消息: ").append(ex.getMessage());
        log.error(stringBuilder.toString());
    }
}
